#ifndef _UTILEX_SAFEPTR_HPP_
#define _UTILEX_SAFEPTR_HPP_

#include <crt_util.h>

#ifdef __cplusplus

struct default_free_safeptr {
    static void* default_value() { return 0; }
    template <class _Ptr> static bool equal_to(_Ptr l, _Ptr r) { return l == r; }
    template <class _Ptr> static void destroy(_Ptr p)
    {
        if (p != NULL) {
            heap_free(p);
            p = NULL;
        }
    }
};

struct default_safeptr {
    static void* default_value() { return 0; }
    template <class _Ptr> static bool equal_to(_Ptr l, _Ptr r) { return l == r; }
    template <class _Ptr> static void destroy(_Ptr p)
    {
        if (p != NULL) {
            delete p;
            p = NULL;
        }
    }
};

struct default_array_safeptr {
    static void* default_value() { return 0; }
    template <class _Ptr> static bool equal_to(_Ptr l, _Ptr r) { return l == r; }
    template <class _Ptr> static void destroy(_Ptr p)
    {
        if (p != NULL) {
            delete[] p;
            p = NULL;
        }
    }
};

template <class _Ptr, class _Traits = default_safeptr> class SafePtr
{
public:
    SafePtr() { m_p = (_Ptr)m_tr.default_value(); }
    SafePtr(_Ptr p) : m_p(p) { }

    SafePtr(const SafePtr& other) { m_p = other.m_p; }
    ~SafePtr() { m_tr.destroy(m_p); }
    SafePtr& operator=(_Ptr p)
    {
        if (!m_tr.equal_to(m_p, p)) {
            m_tr.destroy(m_p);
            m_p = p;
        }
        return *this;
    }
    _Ptr detach()
    {
        _Ptr tmp = m_p;
        m_p = (_Ptr)m_tr.default_value();
        return tmp;
    }
    void dispose()
    {
        m_tr.destroy(m_p);
        m_p = (_Ptr)m_tr.default_value();
    }
    operator _Ptr() const { return m_p; }
    _Ptr operator->() const { return m_p; }

    _Ptr ptr() { return m_p; }
    const _Ptr ptr() const { return m_p; }

public:
    _Ptr m_p;
    _Traits m_tr;
};

#define UCharArrayPtr SafePtr<unsigned char*, default_array_safeptr>
#define CharArrayPtr  SafePtr<char*, default_array_safeptr>
#define UCharPtr      SafePtr<unsigned char*, default_free_safeptr>
#define CharPtr       SafePtr<char*, default_free_safeptr>
#define WCharPtr      SafePtr<wchar_t*, default_free_safeptr>

#if (TARGET_OS == OS_WIN)

struct hmodule_safeptr {
    static HMODULE default_value() { return 0; }
    static bool equal_to(HMODULE l, HMODULE r) { return l == r; }
    static void destroy(HMODULE mod)
    {
        if (mod)
            dllclose(mod);
    }
};

#define DynamiclibPtr SafePtr<HMODULE, hmodule_safeptr>

struct handle_safeptr {
    static HANDLE default_value() { return 0; }
    static bool equal_to(HANDLE l, HANDLE r) { return l == r; }
    static void destroy(HANDLE handle)
    {
        if (handle)
            ::CloseHandle(handle);
    }
};

#define CHandlePtr SafePtr<HANDLE, handle_safeptr>

struct file_handle_safeptr {
    static HANDLE default_value() { return INVALID_HANDLE_VALUE; }
    static bool equal_to(HANDLE l, HANDLE r) { return l == r; }
    static void destroy(HANDLE h)
    {
        if (INVALID_HANDLE_VALUE != h)
            ::CloseHandle(h);
    }
};

#define FileHandlePtr SafePtr<HANDLE, file_handle_safeptr>

struct find_handle_safeptr {
    static HANDLE default_value() { return INVALID_HANDLE_VALUE; }
    static bool equal_to(HANDLE l, HANDLE r) { return l == r; }
    static void destroy(HANDLE h)
    {
        if (INVALID_HANDLE_VALUE != h)
            ::FindClose(h);
    }
};

#define FindFileHandlePtr SafePtr<HANDLE, find_handle_safeptr>

struct socket_handle_safeptr {
    static _sock_t default_value() { return INVALID_SOCKET; }
    static bool equal_to(_sock_t l, _sock_t r) { return l == r; }
    static void destroy(_sock_t h)
    {
        if (INVALID_SOCKET != h)
            ::closesocket(h);
    }
};

#define SocketHandlePtr SafePtr<_sock_t, socket_handle_safeptr>

struct filemapping_safeptr {
    static void* default_value() { return 0; }
    static bool equal_to(HANDLE l, HANDLE r) { return l == r; }
    static void destroy(HANDLE p)
    {
        if (p != NULL)
            ::UnmapViewOfFile(p);
    }
};

#define MappingHandlePtr SafePtr<HANDLE, filemapping_safeptr>

#elif (TARGET_OS == OS_POSIX)

struct hmodule_safeptr {
    static HMODULE default_value() { return 0; }
    static bool equal_to(HMODULE l, HMODULE r) { return l == r; }
    static void destroy(HMODULE h)
    {
        if (h)
            dlclose(h);
    }
};

#define DynamiclibPtr SafePtr<HMODULE, hmodule_safeptr>

struct socket_handle_safeptr {
    static _sock_t default_value() { return INVALID_SOCKET; }
    static bool equal_to(_sock_t l, _sock_t r) { return l == r; }
    static void destroy(_sock_t h)
    {
        if (INVALID_SOCKET != h)
            ::close(h);
    }
};

#define SocketHandlePtr SafePtr<_sock_t, socket_handle_safeptr>

#elif (TARGET_OS == OS_MACH)

struct hmodule_safeptr {
    static HMODULE default_value() { return 0; }
    static bool equal_to(HMODULE l, HMODULE r) { return l == r; }
    static void destroy(HMODULE h)
    {
        if (h)
            dlclose(h);
    }
};

#define DynamiclibPtr SafePtr<HMODULE, hmodule_safeptr>


#elif (TARGET_OS == OS_UNIX)

struct hmodule_safeptr {
    static HMODULE default_value() { return 0; }
    static bool equal_to(HMODULE l, HMODULE r) { return l == r; }
    static void destroy(HMODULE h)
    {
        if (h)
            dlclose(h);
    }
};

#define DynamiclibPtr SafePtr<HMODULE, hmodule_safeptr>


#endif

#endif

#endif
